package com.wisdom.collect.modbus.io;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

public class FastByteArrayOutputStream extends OutputStream {

    private static final Logger logger = LoggerFactory.getLogger(FastByteArrayOutputStream.class);


    private static final int DEFAULT_INIT_SIZE = 100;

    private static final int DEFAULT_BUMP_SIZE = 100;


    protected int count;


    private int bumpLen;


    byte[] buf;


    FastByteArrayOutputStream() {
        buf = new byte[DEFAULT_INIT_SIZE];
        bumpLen = DEFAULT_BUMP_SIZE;
    }


    FastByteArrayOutputStream(int initialSize) {
        buf = new byte[initialSize];
        bumpLen = DEFAULT_BUMP_SIZE;
    }

    public FastByteArrayOutputStream(int initialSize, int bumpSize) {
        buf = new byte[initialSize];
        bumpLen = bumpSize;
    }

    // --- begin ByteArrayOutputStream compatible methods ---

    public int size() {
        return count;
    }


    public void reset() {
        count = 0;
    }

    @Override
    public void write(int b) throws IOException {
        if (count + 1 > buf.length) {
            bump(1);
        }
        buf[count++] = (byte) b;
    }

    @Override
    public void write(byte[] fromBuf) throws IOException {
        int needed = count + fromBuf.length - buf.length;
        if (needed > 0) {
            bump(needed);
        }
        for (byte aFromBuf : fromBuf) {
            buf[count++] = aFromBuf;
        }
    }

    @Override
    public void write(byte[] fromBuf, int offset, int length) throws IOException {

        int needed = count + length - buf.length;
        if (needed > 0) {
            bump(needed);
        }
        int fromLen = offset + length;

        for (int i = offset; i < fromLen; i++) {
            buf[count++] = fromBuf[i];
        }
    }


    public synchronized void writeTo(OutputStream out) throws IOException {
        out.write(buf, 0, count);
    }

    @Override
    public String toString() {
        try {
            return new String(buf, 0, count, "US-ASCII");
        } catch (Exception e) {
            logger.debug("Problem converting bytes to string - {}", e.getMessage());
        }
        return "";
    }


    public String toString(String encoding) throws UnsupportedEncodingException {
        return new String(buf, 0, count, encoding);
    }


    public byte[] toByteArray() {
        byte[] toBuf = new byte[count];
        System.arraycopy(buf, 0, toBuf, 0, count);
        //for (int i = 0; i < count; i++) {
        //  toBuf[i] = buf[i];
        //}
        return toBuf;
    }

    // --- end ByteArrayOutputStream compatible methods ---


    public void toByteArray(byte[] toBuf, int offset) {
        int toLen = (toBuf.length > count) ? count : toBuf.length;
        System.arraycopy(buf, offset, toBuf, offset, toLen - offset);
    }


    public synchronized byte[] getBufferBytes() {
        byte[] dest = new byte[count];
        System.arraycopy(buf, 0, dest, 0, dest.length);
        return dest;
    }


    public int getBufferOffset() {
        return 0;
    }


    public int getBufferLength() {
        return count;
    }


    public void makeSpace(int sizeNeeded) {
        int needed = count + sizeNeeded - buf.length;
        if (needed > 0) {
            bump(needed);
        }
    }


    public void addSize(int sizeAdded) {
        count += sizeAdded;
    }

    private void bump(int needed) {

        byte[] toBuf = new byte[buf.length + needed + bumpLen];

        System.arraycopy(buf, 0, toBuf, 0, count);
        buf = toBuf;
    }

}